CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/pages/lang/[locale]/index.tsx
Views: 791
1
/*
2
* This file is part of CoCalc: Copyright © 2024 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { DownOutlined } from "@ant-design/icons";
7
import type { MenuProps } from "antd";
8
import { Button, Dropdown, Layout } from "antd";
9
10
import { GetServerSidePropsContext } from "next";
11
12
import Trans from "next-translate/Trans";
13
import useTranslation from "next-translate/useTranslation";
14
15
import { getI18nMessages } from "locales/lib";
16
import { LOCALE, query2locale } from "locales/misc";
17
18
// The I18nProvider is either english by default, or based on the query path: /lang/[locale]
19
import I18nProvider from "next-translate/I18nProvider";
20
21
import AIAvatar from "@cocalc/frontend/components/ai-avatar";
22
import { LOCALIZATIONS } from "@cocalc/util/i18n";
23
import { COLORS } from "@cocalc/util/theme";
24
25
import Content from "components/landing/content";
26
import Footer from "components/landing/footer";
27
import Head from "components/landing/head";
28
import Header from "components/landing/header";
29
import Image from "components/landing/image";
30
import Info from "components/landing/info";
31
import Pitch from "components/landing/pitch";
32
import { Tagline } from "components/landing/tagline";
33
import { SHADOW } from "components/landing/util";
34
import Logo from "components/logo";
35
import { CSS, Paragraph, Title } from "components/misc";
36
import A from "components/misc/A";
37
import { Customize, useCustomize } from "lib/customize";
38
import withCustomize from "lib/with-customize";
39
import basePath from "lib/base-path";
40
41
import SAGEMATH_JUPYTER from "public/cocalc-sagemath-2024-11-22-nq8.png";
42
import assignments from "public/features/cocalc-course-assignments-2019.png";
43
import LatexEditorImage from "public/features/cocalc-latex-editor-2019.png";
44
import RTC from "public/features/cocalc-real-time-jupyter.png";
45
import CHATROOM from "/public/features/chatroom.png";
46
import JupyterTF from "/public/features/cocalc-jupyter2-20170508.png";
47
import terminal from "/public/features/terminal.png";
48
import { join } from "path";
49
50
const HEADER_LEVEL = 3;
51
52
function Nav() {
53
const { t, lang } = useTranslation("index");
54
55
const items: MenuProps["items"] = [...LOCALE]
56
.sort((a, b) => LOCALIZATIONS[a].name.localeCompare(LOCALIZATIONS[b].name))
57
.map((locale, idx) => {
58
const l = LOCALIZATIONS[locale];
59
return {
60
key: idx,
61
label: (
62
<A href={`/${locale}`}>
63
{l.flag} {l.native} ({l.name})
64
</A>
65
),
66
};
67
});
68
69
const l = LOCALIZATIONS[lang];
70
const color = COLORS.GRAY_DD;
71
return (
72
<div
73
style={{
74
padding: "5px 20px",
75
backgroundColor: COLORS.YELL_LLL,
76
color,
77
}}
78
>
79
{t("translated-info", {
80
lang: l.native,
81
})}{" "}
82
<A href={"/"}>{t("home-page")}</A>.
83
<span style={{ float: "right" }}>
84
<Dropdown
85
menu={{ items }}
86
trigger={["click"]}
87
placement={"bottomRight"}
88
overlayStyle={{ maxHeight: "75vh", overflow: "auto", ...SHADOW }}
89
>
90
<a onClick={(e) => e.preventDefault()} style={{ color }}>
91
{l.flag} {l.native} ({l.name}) <DownOutlined />
92
</a>
93
</Dropdown>
94
</span>
95
</div>
96
);
97
}
98
99
function Features() {
100
const { onCoCalcCom } = useCustomize();
101
const { t } = useTranslation("index");
102
103
function vendorOpenWorld() {
104
return (
105
<Pitch
106
style={{ backgroundColor: COLORS.YELL_LLL }}
107
col1={
108
<Info
109
anchor="a-vendor-lockin"
110
icon="lock"
111
level={HEADER_LEVEL}
112
title={t("no-vendor-lockin")}
113
>
114
<Trans
115
i18nKey="index:no-vendor-lockin-text"
116
components={{ ul: <ul />, li: <li />, strong: <strong /> }}
117
/>
118
</Info>
119
}
120
col2={
121
<Info
122
anchor="a-open-world"
123
icon="global"
124
level={HEADER_LEVEL}
125
title={t("open-world-title")}
126
>
127
<Trans
128
i18nKey="index:open-world-text"
129
components={{ ul: <ul />, li: <li />, strong: <strong /> }}
130
/>
131
</Info>
132
}
133
/>
134
);
135
}
136
137
function realtimeChat() {
138
return (
139
<Pitch
140
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
141
col1={
142
<Info
143
level={HEADER_LEVEL}
144
title={t("realtime-collaboration")}
145
icon="users"
146
anchor="a-realtimesync"
147
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
148
belowWide={true}
149
icons={[
150
{ icon: "jupyter", link: "/features/jupyter-notebook" },
151
{ icon: "tex", title: "LaTeX", link: "/features/latex-editor" },
152
{
153
icon: "slides",
154
title: "Whiteboard",
155
link: "/features/whiteboard",
156
},
157
]}
158
>
159
<Paragraph>
160
<Image shadow alt={"Realtime collaboration"} src={RTC} />
161
</Paragraph>
162
<Trans
163
i18nKey="index:realtime-collaboration-text"
164
components={{ strong: <strong />, p: <Paragraph /> }}
165
/>
166
</Info>
167
}
168
col2={
169
<Info
170
level={HEADER_LEVEL}
171
title={t("chat-title")}
172
icon="comment"
173
anchor="a-chat-title"
174
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
175
belowWide={true}
176
icons={[
177
{ icon: "comment", link: "https://doc.cocalc.com/chat.html" },
178
{
179
icon: <AIAvatar size={32} />,
180
title: "LLM",
181
link: "/features/ai",
182
},
183
]}
184
>
185
<Paragraph>
186
<Image shadow alt={"Chatroom"} src={CHATROOM} />
187
</Paragraph>
188
<Trans
189
i18nKey="index:chat-text"
190
components={{
191
p: <Paragraph />,
192
ul: <ul />,
193
li: <li />,
194
strong: <strong />,
195
A: <A href="https://doc.cocalc.com/chat.html" />,
196
}}
197
/>
198
</Info>
199
}
200
/>
201
);
202
}
203
204
function softwareCompute() {
205
return (
206
<Pitch
207
style={{ backgroundColor: COLORS.YELL_LLL }}
208
col1={
209
<Info
210
level={HEADER_LEVEL}
211
title={t("many-languages")}
212
icon="flow-chart"
213
anchor="a-many-languages"
214
icons={[
215
{ icon: "julia", link: "/features/julia" },
216
{ icon: "linux", link: "/features/linux" },
217
{ icon: "python", link: "/features/python" },
218
{ icon: "r", link: "/features/r-statistical-software" },
219
{ icon: "sagemath", title: "SageMath", link: "/features/sage" },
220
{ icon: "octave", link: "/features/octave" },
221
]}
222
>
223
<Trans
224
i18nKey="index:many-languages-text"
225
components={{
226
strong: <strong />,
227
a: <A href="https://doc.cocalc.com/howto/index.html" />,
228
}}
229
/>
230
</Info>
231
}
232
col2={
233
<Info
234
level={HEADER_LEVEL}
235
title={t("compute-servers-title")}
236
icon="server"
237
anchor="a-compute-servers"
238
icons={[
239
{
240
icon: "nvidia",
241
title: "GPUs",
242
link: "https://doc.cocalc.com/compute_server.html",
243
},
244
{
245
icon: "pytorch",
246
title: "PyTorch",
247
link: "https://doc.cocalc.com/compute_server.html",
248
},
249
{
250
icon: "tensorflow",
251
title: "TensorFlow",
252
link: "https://doc.cocalc.com/compute_server.html",
253
},
254
{
255
icon: "vscode",
256
title: "VS Code",
257
link: "https://doc.cocalc.com/vscode.html",
258
},
259
{
260
icon: "desktop",
261
title: "X11 Desktop",
262
link: "features/x11",
263
},
264
{
265
icon: "terminal",
266
title: "Linux Terminal",
267
link: "features/terminal",
268
},
269
]}
270
>
271
<Trans
272
i18nKey="index:compute-servers-text"
273
components={{
274
strong: <strong />,
275
p: <Paragraph />,
276
A1: <A href="https://doc.cocalc.com/compute_server.html" />,
277
A2: (
278
<A href="https://github.com/sagemathinc/cocalc-howto/blob/main/README.md" />
279
),
280
}}
281
/>
282
</Info>
283
}
284
/>
285
);
286
}
287
288
function jupyterLatex() {
289
return (
290
<Pitch
291
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
292
col1={
293
<Info
294
level={HEADER_LEVEL}
295
title={t("jupyter-notebook-title")}
296
icon="jupyter"
297
anchor="a-jupyter-notebook"
298
wide
299
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
300
icons={[
301
{ icon: "jupyter", link: "/features/jupyter-notebook" },
302
{
303
icon: "pencil",
304
title: "NBGrader",
305
link: "https://doc.cocalc.com/teaching-nbgrader.html",
306
},
307
{
308
icon: <AIAvatar size={32} />,
309
title: "LLM",
310
link: "/features/ai",
311
},
312
]}
313
>
314
<Paragraph>
315
<Image shadow alt={"Jupyter Notebook"} src={JupyterTF} />
316
</Paragraph>
317
<Trans
318
i18nKey="index:jupyter-notebook-text"
319
components={{
320
strong: <strong />,
321
a: <A href="/features/jupyter-notebook" />,
322
A2: <A href="/features/teaching" />,
323
AI: <A href="/features/ai" />,
324
li: <li />,
325
ul: <ul />,
326
p: <Paragraph />,
327
}}
328
/>
329
</Info>
330
}
331
col2={
332
<Info
333
level={HEADER_LEVEL}
334
title={t("latex-editor-title")}
335
icon="tex"
336
anchor="a-latex-editor"
337
wide
338
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
339
icons={[
340
{ icon: "tex", title: "LaTeX", link: "/features/latex-editor" },
341
]}
342
>
343
<Paragraph>
344
<Image shadow alt={"LaTeX Editor"} src={LatexEditorImage} />
345
</Paragraph>
346
<Trans
347
i18nKey="index:latex-editor-text"
348
components={{
349
strong: <strong />,
350
a: <A href="/features/latex-editor" />,
351
li: <li />,
352
ul: <ul />,
353
p: <Paragraph />,
354
}}
355
/>
356
</Info>
357
}
358
/>
359
);
360
}
361
362
function teachingLinux() {
363
return (
364
<Pitch
365
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
366
col1={
367
<Info
368
level={HEADER_LEVEL}
369
title={t("linux-title")}
370
icon="jupyter"
371
anchor="a-linux"
372
wide
373
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
374
icons={[
375
{
376
icon: "terminal",
377
title: "Linux Terminal",
378
link: "/features/terminal",
379
},
380
{ icon: "servers", title: "Software", link: "/software" },
381
]}
382
>
383
<Paragraph>
384
<Image shadow alt={"Linux Terminal"} src={terminal} />
385
</Paragraph>
386
<Trans
387
i18nKey="index:linux-text"
388
components={{
389
strong: <strong />,
390
A: <A href="/features/terminal" />,
391
A2: <A href="/software/executables" />,
392
li: <li />,
393
ul: <ul />,
394
p: <Paragraph />,
395
}}
396
/>
397
</Info>
398
}
399
col2={
400
<Info
401
level={HEADER_LEVEL}
402
title={t("teaching-title")}
403
icon="graduation-cap"
404
anchor="a-teaching"
405
wide
406
style={{ backgroundColor: COLORS.ANTD_BG_BLUE_L }}
407
icons={[
408
{
409
icon: "graduation-cap",
410
title: "Teaching",
411
link: "/features/teaching",
412
},
413
{
414
icon: "pencil",
415
title: "NBGrader",
416
link: "https://doc.cocalc.com/teaching-nbgrader.html",
417
},
418
]}
419
>
420
<Paragraph>
421
<Image
422
shadow
423
alt={"CoCalc Course Management"}
424
src={assignments}
425
/>
426
</Paragraph>
427
<Trans
428
i18nKey="index:teaching-text"
429
components={{
430
strong: <strong />,
431
A: <A href="/features/teaching" />,
432
li: <li />,
433
ul: <ul />,
434
p: <Paragraph />,
435
}}
436
/>
437
</Info>
438
}
439
/>
440
);
441
}
442
443
function gettingStarted() {
444
const style: CSS = { color: COLORS.GRAY_LL } as const;
445
446
if (onCoCalcCom) {
447
return (
448
<Pitch
449
title={t("getting-started")}
450
style={{ backgroundColor: COLORS.BLUE_D, ...style }}
451
col1={
452
<Info
453
level={HEADER_LEVEL}
454
title={t("gettingstarted-saas")}
455
anchor="a-saas"
456
style={{ backgroundColor: COLORS.BLUE_D }}
457
textStyle={style}
458
>
459
<Trans
460
i18nKey="index:gettingstarted-saas-text"
461
components={{
462
strong: <strong />,
463
A2: <A style={style} href="/auth/sign-up" />,
464
A: <A style={style} href="/store" />,
465
li: <li style={style} />,
466
ul: <ul />,
467
p: <Paragraph style={style} />,
468
}}
469
/>
470
<Paragraph style={{ textAlign: "center" }}>
471
<Button
472
ghost
473
size="large"
474
style={{ fontWeight: "bold" }}
475
onClick={() =>
476
(window.location.href = join(basePath, "/auth/sign-up"))
477
}
478
title={t("sign-up")}
479
>
480
{t("sign-up")}
481
</Button>
482
</Paragraph>
483
</Info>
484
}
485
col2={
486
<Info
487
level={HEADER_LEVEL}
488
title={t("gettingstarted-onprem")}
489
anchor="a-onprem"
490
style={{ backgroundColor: COLORS.BLUE_D }}
491
textStyle={style}
492
>
493
<Trans
494
i18nKey="index:gettingstarted-onprem-text"
495
components={{
496
strong: <strong />,
497
A: <A style={style} href="https://onprem.cocalc.com" />,
498
li: <li style={style} />,
499
ul: <ul />,
500
p: <Paragraph style={style} />,
501
}}
502
/>
503
<Paragraph style={{ textAlign: "center" }}>
504
<Button
505
ghost
506
size="large"
507
style={{ fontWeight: "bold" }}
508
onClick={() =>
509
(window.location.href = join(basePath, "/pricing/onprem"))
510
}
511
title={"On-Premises"}
512
>
513
{t("gettingstarted-onprem")}
514
</Button>
515
</Paragraph>
516
</Info>
517
}
518
/>
519
);
520
} else {
521
return (
522
<Info
523
level={HEADER_LEVEL}
524
title={t("getting-started")}
525
anchor="a-signup"
526
style={{ backgroundColor: COLORS.BLUE_D }}
527
textStyle={style}
528
>
529
<Trans
530
i18nKey="index:gettingstarted-signup-text"
531
components={{
532
strong: <strong />,
533
A: <A style={style} href="/auth/sign-up" />,
534
li: <li />,
535
ul: <ul />,
536
p: <Paragraph style={{ ...style, textAlign: "center" }} />,
537
}}
538
/>
539
<Paragraph style={{ textAlign: "center" }}>
540
<Button
541
ghost
542
size="large"
543
style={{ fontWeight: "bold" }}
544
onClick={() =>
545
(window.location.href = join(basePath, "/auth/sign-up"))
546
}
547
title={t("sign-up")}
548
>
549
{t("sign-up")}
550
</Button>
551
</Paragraph>
552
</Info>
553
);
554
}
555
}
556
557
return (
558
<>
559
{realtimeChat()}
560
{softwareCompute()}
561
{jupyterLatex()}
562
{vendorOpenWorld()}
563
{teachingLinux()}
564
{gettingStarted()}
565
</>
566
);
567
}
568
569
function Hello({ customize }) {
570
const { t } = useTranslation("index");
571
572
const { siteName } = customize;
573
574
function intro() {
575
return (
576
<>
577
<Title level={2}>{t("intro")}</Title>{" "}
578
<Paragraph>
579
<Trans i18nKey="index:intro-1" components={{ strong: <strong /> }} />
580
</Paragraph>
581
<Paragraph>
582
<Image
583
alt={t('screenshot')}
584
src={SAGEMATH_JUPYTER}
585
shadow={true}
586
/>
587
</Paragraph>
588
</>
589
);
590
}
591
592
return (
593
<>
594
<Head title={`${t("site-description")} – ${siteName}`} />
595
<Layout>
596
<Header />
597
<Nav />
598
<Layout.Content style={{ backgroundColor: "white" }}>
599
<Content
600
style={{ minHeight: "30vh" }}
601
body={<Logo type="full" style={{ width: "50%" }} />}
602
title={siteName}
603
subtitle={t("site-description")}
604
imageAlternative={intro()}
605
/>
606
<Tagline value={t("tagline")} style={{ padding: "5px" }} />
607
<Features />
608
<Nav />
609
<Footer />
610
</Layout.Content>
611
</Layout>
612
</>
613
);
614
}
615
616
export default function I18NIndexPage({ customize, locale, messages }) {
617
return (
618
<Customize value={customize}>
619
<I18nProvider lang={locale} namespaces={messages}>
620
<Hello customize={customize} />
621
</I18nProvider>
622
</Customize>
623
);
624
}
625
626
export async function getServerSideProps(context: GetServerSidePropsContext) {
627
const locale = query2locale(context.query);
628
const messages = await getI18nMessages(locale);
629
630
return withCustomize({
631
context,
632
props: { locale, messages },
633
});
634
}
635
636